#![no_std]
#![no_main]
extern crate alloc;

use alloc::boxed::Box;
use alloc::rc::Rc;
use core::cell::RefCell;
use core::fmt::Debug;
use display_interface_spi::SPIInterfaceNoCS;
use embedded_hal::prelude::_embedded_hal_blocking_delay_DelayMs;
use esp_hal::clock::{CpuClock, RadioClockController};
use esp_hal::delay::Delay;
use esp_hal::{main, spi};
use esp_hal::gpio::{Level, Output, OutputConfig};
use esp_hal::peripherals::Peripherals;
use esp_hal::rtc_cntl::Rtc;
use esp_hal::spi::master::Config;
use esp_hal::spi::master::Spi;
use esp_hal::time::{Duration, Instant, Rate};
use esp_hal::timer::systimer::SystemTimer;
use esp_hal::timer::timg::TimerGroup;
use mipidsi::Display;
use slint::platform::{Platform, WindowAdapter};
use slint::PlatformError;

#[panic_handler]
fn panic(_: &core::panic::PanicInfo) -> ! {
    loop {}
}


slint::slint!(
    export component MainView {
    width: 128px;
    height: 64px;
    Text {
        text: "hello world";
        color: greenyellow;
    }
    Rectangle {
        x:0px;
        y:0px;
        width: 100px;
        height: 40px;
        background: yellow;
        Text {
          text: "hello rectangle";
          color: black;
        }

    }
}
);



static ALLOCATOR: esp_alloc::EspHeap = esp_alloc::EspHeap::empty();

#[main]
fn main() -> ! {
    // generator version: 0.3.1



    MainView::new().unwrap().run().unwrap();
    loop {
        let delay_start = Instant::now();
        while delay_start.elapsed() < Duration::from_millis(500) {}
    }

    // for inspiration have a look at the examples at https://github.com/esp-rs/esp-hal/tree/esp-hal-v1.0.0-beta.0/examples/src/bin
}

// Initializes the heap and sets the Slint platform.
pub fn init() {


    // Initialize peripherals first.
    let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::_80MHz));

    // Initialize the PSRAM allocator.
    // esp_alloc::psram_allocator!(peripherals.PSRAM, esp_hal::psram);

    // Create an EspBackend that now owns the peripherals.
    slint::platform::set_platform(Box::new(EspBackend {
        window: RefCell::new(None),
    }))
        .expect("backend already initialized");
}

struct EspBackend {
    window: RefCell<Option<Rc<slint::platform::software_renderer::MinimalSoftwareWindow>>>,
}

impl Platform for EspBackend {
    fn create_window_adapter(
        &self,
    ) -> Result<Rc<dyn WindowAdapter>, PlatformError> {
        let window = slint::platform::software_renderer::MinimalSoftwareWindow::new(
            slint::platform::software_renderer::RepaintBufferType::ReusedBuffer,
        );
        self.window.replace(Some(window.clone()));
        Ok(window)
    }


    fn run_event_loop(&self) -> Result<(), PlatformError> {
        let peripherals = esp_hal::init(esp_hal::Config::default().with_cpu_clock(CpuClock::_80MHz));

        let a = peripherals.SYSTEM;
        let clocks = RadioClockController::new(peripherals.RADIO_CLK);
        let mut rtc = Rtc::new(peripherals.LPWR);
        let timer_group0 = TimerGroup::new(peripherals.TIMG0);
        let mut  wdt0 = timer_group0.wdt;
        let timer_group1 = TimerGroup::new(peripherals.TIMG1);
        let mut  wdt1 = timer_group1.wdt;

        // let mut system = peripherals.SYSTEM.split();
        // let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze();
        //
        // let mut rtc = Rtc::new(peripherals.RTC_CNTL);
        // let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
        // let mut wdt0 = timer_group0.wdt;
        // let timer_group1 = TimerGroup::new(peripherals.TIMG1, &clocks);
        // let mut wdt1 = timer_group1.wdt;

        rtc.rwdt.disable();
        wdt0.disable();
        wdt1.disable();

        let mut delay = Delay::new();
        // let io = IO::new(peripherals.GPIO, peripherals.IO_MUX);

        let clk = peripherals.GPIO18;
        let sdo = peripherals.GPIO17;
        let cs = peripherals.GPIO14;


        let mut spi = Spi::new(peripherals.SPI2,
                               Config::default()
                                   .with_frequency(Rate::from_khz(60))
                                   .with_mode(spi::Mode::_0)).unwrap()
            .with_sck(clk)
            .with_miso(sdo)
            .with_cs(cs)
            .with_dma(peripherals.DMA_CH0);
        // let spi = Spi::new_no_miso(
        //     peripherals.SPI2,
        //     clk,
        //     sdo,
        //     cs,
        //     60u32.MHz(),
        //     SpiMode::Mode0,
        //     &clocks,
        // );
        // println!("spi init.");

        // let dc = io.pins.gpio15.into_push_pull_output();
        // let rst = io.pins.gpio16.into_push_pull_output();

        let dc = Output::new(peripherals.GPIO15,Level::Low,OutputConfig::default());
        let rst = Output::new(peripherals.GPIO16,Level::Low,OutputConfig::default());

        // let di = SPIInterfaceNoCS::new(spi, dc);

        let mut delay = embassy_time::Delay;
        loop{}
        // let display = mipidsi::Builder::st7789(di).init(&mut delay, Some(rst)).unwrap();

        // let mut display = mipidsi::Builder::st7789(di)
        //     .with_display_size(240, 280)
        //     .with_window_offset_handler(|_| (0, 20))
        //     .with_framebuffer_size(240, 280)
        //     .with_invert_colors( mipidsi::ColorInversion::Inverted)
        //     .init(&mut delay , Some(rst))
        //     .unwrap();

        // println!("display init.");
        // let mut bl = io.pins.gpio13.into_push_pull_output();
        /*let mut bl = Output::new(peripherals.GPIO13,Level::Low,OutputConfig::default());
        bl.set_high();

        let size = slint::PhysicalSize::new(240, 280);

        self.window.borrow().as_ref().unwrap().set_size(size);

        let mut buffer_provider = DrawBuffer {
            display,
            buffer: &mut [slint::platform::software_renderer::Rgb565Pixel::default(); 240],
        };

        loop {
            slint::platform::update_timers_and_animations();

            if let Some(window) = self.window.borrow().clone() {
                window.draw_if_needed(|renderer| {
                    renderer.render_by_line(&mut buffer_provider);
                });
                if window.has_active_animations() {
                    continue;
                }
            }
        }*/
    }

    fn debug_log(&self, arguments: core::fmt::Arguments) {
        // println!("{}", arguments);
    }
}

struct DrawBuffer<'a, Display> {
    display: Display,
    buffer: &'a mut [slint::platform::software_renderer::Rgb565Pixel],
}

impl<DI: display_interface::WriteOnlyDataCommand, RST: embedded_hal::digital::v2::OutputPin>
slint::platform::software_renderer::LineBufferProvider
for &mut DrawBuffer<'_, Display<DI, mipidsi::models::ST7789, RST>>
{
    type TargetPixel = slint::platform::software_renderer::Rgb565Pixel;

    fn process_line(
        &mut self,
        line: usize,
        range: core::ops::Range<usize>,
        render_fn: impl FnOnce(&mut [slint::platform::software_renderer::Rgb565Pixel]),
    ) {
        let buffer = &mut self.buffer[range.clone()];

        render_fn(buffer);

        // We send empty data just to get the device in the right window
        self.display
            .set_pixels(
                range.start as u16,
                line as _,
                range.end as u16,
                line as u16,
                buffer
                    .iter()
                    .map(|x| embedded_graphics_core::pixelcolor::raw::RawU16::new(x.0).into()),
            )
            .unwrap();
    }
}

